# FTP operations
# (c) 2008-2009 NIXON Software Corporation.

from ftplib import FTP, error_temp, error_perm, error_proto
import transfer, local, format, remote, browser
import os, sys, socket
import wx
import wx.lib.delayedresult as delayedResult
from alert import alertBox

class newFTP(FTP):
	# do not print commands
	
	def __init__(self, host='', user='', passwd='', acct='',
				 timeout=15, frame=None):
		self.timeout = timeout
		if host:
			self.connect(host)
			if user:
				self.login(user, passwd, acct)
				
		self.frame=frame
		
	def sanitize(self, s):
		if s[:5] == 'pass ' or s[:5] == 'PASS ':
			i = len(s)
			while i > 5 and s[i-1] in '\r\n':
				i = i-1
			s = s[:5] + '*'*(i-5) + s[i:]
		return s

	def putline(self, line):
		line = line + '\r\n'
		if self.debugging > 1: print(self.sanitize(line))
		self.sock.sendall(line)

	def putcmd(self, line):
		if self.debugging: print(self.sanitize(line))
		self.putline(line)
		
	def getresp(self):
		resp = self.getmultiline()
		if self.debugging: print(self.sanitize(resp))
		self.lastresp = resp[:3]
		c = resp[:1]
		if c in ('1', '2', '3'):
			return resp
		if c == '4':
			raise error_temp, resp
		if c == '5':
			raise error_perm, resp
		raise error_proto, resp
		
try:
	import M2Crypto
	from ftpslib import FTP_TLS, secureSocket_m2crypto
	blSSL = True
except:
	print sys.exc_info()[1]
	blSSL = False
	
class keepAlive(wx.Timer):
	def __init__(self, prnt):
		wx.Timer.__init__(self)
		self.prnt = prnt
		
	def Notify(self):
		self.prnt.tl.addTask(self.prnt.hftp.sendcmd, ('NOOP',))
		
class ftpMaker():
	def __init__(self, tpAcct, handle, pasv=True, ssl=False):
		tpAcct[0].replace('/','')
		tpAcct[0].replace('ftp:','')
		self.host = tpAcct[0]
		self.user = tpAcct[1]
		self.passwd = tpAcct[2]
		self.acct = tpAcct[3]
		self.pasv = pasv
		self.hdlFrame = handle
		self.tl = remote.taskList(handle)
		self.ssl = ssl
		self.keepAlive = keepAlive(self)
		
		if blSSL:
			strSSL = ''
		else:
			strSSL = 'not '
		
		print ('NOTE: FTP over SSL is ' + strSSL + 'available.')
	
		delayedResult.startWorker(self.connectConsumer, self.connect, wargs=())
		
	def connect(self):
		strMessage = 'Connecting to ' + self.host + '...'
		print(strMessage)
		self.hdlFrame.pnRemote.statusText.SetLabel(strMessage)
		
		if self.ssl:
			self.hftp = FTP_TLS(secureSocket_m2crypto(), self.host)
		else:
			self.hftp = newFTP(self.host, timeout=10000, frame=self.hdlFrame)
			
		self.tr = transfer.main(self.hdlFrame, self.hftp)
		self.hftp.set_debuglevel(1)
		self.hftp.set_pasv(self.pasv)
		
	def connectConsumer(self, delayedresult):
		try:
			result = delayedresult.get()
		except socket.gaierror:
			strMessage = 'Shuttle can\'t find the server "%s"' % self.host
			print(strMessage)
			dlg = alertBox(self.hdlFrame, strMessage, \
				'Make sure that the hostname is valid and that you spelled it correctly.')
			self.hdlFrame.pnRemote.doStatus(False)
			return
			
		delayedResult.startWorker(self.loginConsumer, self.login, wargs=())
		
	def login(self):
		if self.ssl:
			self.hftp.auth_tls()
			self.hftp.prot_p()
		
		self.hftp.login(self.user, self.passwd, self.acct)
		
	def verifyCredentials(self):
		if self.passwd == '' and self.user != 'anonymous' and self.user != '':
			response = ''
		
			dlg = wx.TextEntryDialog(self.hdlFrame, 'Please enter your password.',
					'Login', style=wx.TE_PASSWORD)
			if dlg.ShowModal() == wx.ID_OK:
				response = dlg.GetValue()
				dlg.Destroy()
				
			self.passwd = response
			
	def loginConsumer(self, delayedresult):
		try:
			result = delayedresult.get()
		except error_perm, e:
			strMessage = 'Shuttle couldn\'t log in to the server %s.' % self.host
			print(strMessage)
			dlg = alertBox(self.hdlFrame, strMessage, \
				'Make sure your username and password are entered correctly. If the server only accepts anonymous connections, you cannot log in with a username and password.')
			self.hdlFrame.pnRemote.doStatus(False)
			return
		
		strType = self.hftp.sendcmd('SYST')
		self.intType = 0
		self.hdlFrame.dirList = remote.dirListing(self.hdlFrame)
		self.hdlFrame.UpdateList('')
		self.keepAlive.Start(30000)
		browser.preview.transfer = self.tr
		
	def chgDir(self, path):
		try:
			self.hftp.cwd(path)
		except Exception, e:
			print(e)
			errNum = e.args[0][:3]
			if errNum == '421': #421 idle timeout
				self.connect()
				self.login()
			else:
				raise e
			
	def renameFile(self, path, strFrom, strTo, num):
		self.chgDir(path)
		self.hftp.rename(strFrom, strTo)
		self.hdlFrame.reloadRemote(num-1,)

	def currDir(self):
		return self.hftp.pwd()
		
	def createDir(self, pwf, box):
		self.box = box
		self.tl.addTask(self.hftp.mkd, (pwf,), self.dirConsumer)
		
	def dirConsumer(self, delayedresult):
		try:
			s = delayedresult.get()
			self.hdlFrame.reloadRemote(self.box.Number-1)
		except error_perm, e:
			lbl = self.box.Label
			if lbl == '': lbl = '/'
			dlg = alertBox(self.hdlFrame, \
				'You do not have permission to create a folder in "' + lbl + '".', \
				'Make sure that you have the appropriate access privileges and that there isn\'t a folder in "' + lbl + '" with the same name.')

	def deleteFile(self, pwf, box=None):
		self.pwf = pwf
		self.tl.addTask(self.hftp.delete, (pwf,), self.deleteConsumer)
		
	def deleteConsumer(self, delayedresult):
		try:
			s = delayedresult.get()
			self.hdlFrame.reloadRemote(self.box.Number-1)
		except error_perm, e:
			dlg = alertBox(self.hdlFrame, \
				'You do not have permission to delete "' + self.pwf + '".', \
				'Make sure that you have the appropriate access privileges.')
		except Exception, e:
			print(e)
		
	def transfer(self, dir, tpFiles, handle, upload=False, box=None, pathOverride=''):
		if pathOverride != '':
				path = pathOverride
		elif box:
			path = box.Label
		else:
			if upload: path = handle.pnRemote.lstBrowsers[-1].Label
			else: path = handle.pnLocal.lstBrowsers[-1].Label

			
		for x in tpFiles:
			self.tl.block = True
			#realName = format.getRealName(handle, dir, x, upload)
			
			pwf = dir + '/' + x
			if upload:
				finfo = local.getType(pwf)
			else:
				finfo = handle.dirList.getInfo(pwf)[0][:1]
				
			dirName = path + '/' + x

			if finfo == 'd' or finfo == 2:
				handle.doQueue(True)
				if upload:
					self.createDir(dirName, handle.pnRemote.lstBrowsers[-1])
					callback = self.uploadTree
				else:
					try: os.mkdir(dirName)
					except: pass
					callback = self.xferTree
				
				if box:
					print box
					if box.FindItem(-1, x) == -1:
						box.InsertImageStringItem(box.GetItemCount() + 1,x, 2)
					
				self.tl.addTask(callback, (pwf, dirName, handle, box))
				
			else:
				id = self.tl.getNewId()
				if upload:
					self.tl.addTask(self.tr.transferFile, (1, pwf, path, box))
				else:
					self.tl.addTask(self.tr.transferFile, (0, pwf, path, box))

	def deleteTree(self, top, box=None): # from Python os.stat docs
		self.hdlFrame.dirList.doPath(top, self)

		names = self.hdlFrame.dirList.getNames(top)
		ln = len(names)
		if ln != 0:
			for f in names:
				pwf = top + '/' + f
				if self.hdlFrame.dirList.getInfo(pwf)[0][:1] == 'd':
					self.deleteTree(pwf, self.hdlFrame)
				else:
					self.hftp.delete(pwf)

				wx.Yield()

		self.chgDir('/')
		self.hftp.rmd(top)
	
	def xferTree(self, top, localpath, handle, box):	
		handle.dirList.doPath(top, self);
		names = handle.dirList.getNames(top)

		ln = len(names)
		if ln != 0:
			for f in names:
				handle.stTransName.SetLabel('undefined')
				handle.gaugeProgress.Pulse()
				if top == '/':
					strpath = ''
				else:
					strpath = top + '/'
					
				pwf = strpath + f
				dirpath = localpath + '/' + f
				if handle.dirList.getInfo(pwf)[0][:1] == 'd':
					# It's a directory, recurse into it
					os.mkdir(dirpath)
					self.xferTree(pwf, dirpath, handle, box)
				else:
					self.tr.transferFile(0, pwf, localpath, box)
					
		self.tl.block = False

	def uploadTree(self, top, remotePath, handle, box=None):
		names = local.getNames(top)

		ln = len(names)
		if ln != 0:
			for f in names:
				pwf = top + '/' + f
				if local.getType(pwf) == 2:
					# It's a directory, recurse into it
					self.hftp.mkd(remotePath + '/' + f)
						
					self.uploadTree(pwf, remotePath + '/' + f, handle)
				else:
					self.tr.transferFile(1, pwf, remotePath, box)